package com.wx.clazzloader.hotreload;

import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 容器对象
 * @description:
 * @author: wangxun
 * @createDate: 2021/12/27
 * @version: 1.0
 */
@Slf4j
public class MyContainer {
    static Map<String, Object> container = new HashMap<>();

    static Map<String, ReloadFile> LAST_MODIFIED = new ConcurrentHashMap<>();




    /** 根路径 */
    public static final String BASE_PACKAGE = "E:" + File.separator +"clazz" +File.separator ;
    /**
     * 后缀
     */
    public static final String CLASS_FILE_SUFFIX  = ".class";

    static MyContainer instance = new MyContainer();
    private MyContainer(){
        try {
            // init 初始化 扫描
            init();
            //开启监控
            monitor();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 扫描
     */
    private void init() {

        // 包路径下的根目录
        File packageFile = new File(BASE_PACKAGE);

        try {
            addClass(packageFile);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }


    }

    /**
     * 添加全部的类文件
     * @param packageFile
     */
    private void addClass(File packageFile) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        if(packageFile.isDirectory()){
            File[] files = packageFile.listFiles();

            for (File file : files) {
                if(file.isDirectory()){
                    addClass(file);
                }else {

                    if (file.getName().endsWith(CLASS_FILE_SUFFIX)) {
                        String suffixName = file.getName().replace(CLASS_FILE_SUFFIX,"");

                        // 加载类
                        ReLoadClassLoader loader = new ReLoadClassLoader();

                        Class<?> aClass = loader.findClass(suffixName);
                        // 将类加载到容器中
                        container.put(aClass.getSimpleName(), aClass.newInstance());
                        // 将类的最后修改时间纪录
                        LAST_MODIFIED.put(aClass.getSimpleName(), new ReloadFile(file,file.lastModified()));
                    }

                }
            }
        }
    }

    static MyContainer getInstance(){
         return instance;
   }


    <T> T getObject(String name){
        Object proxy = container.get(name);
        if(proxy == null){
            throw new RuntimeException("生成对象出错，此类型不在管理范围");
        }
        return (T) proxy;
    }


    static void monitor(){
        new Thread(()->{
            log.info("{}-》监控线程开启",Thread.currentThread().getName());
            while (true){

                for (Map.Entry<String, ReloadFile> classReloadFileEntry : LAST_MODIFIED.entrySet()) {
                    ReloadFile file = classReloadFileEntry.getValue();
                    long now = file.getFile().lastModified();
                    if(now > file.getLastModified()){
                        // 修改过，那么就重新加载

                        log.info("{}-》被修改过，开启热部署",classReloadFileEntry.getKey());

                        try {
                            ReLoadClassLoader loader = new ReLoadClassLoader();
                            Class<?> aClass1 = loader.findClass(classReloadFileEntry.getKey());

                            // 将类加载到容器中
                            container.put(aClass1.getSimpleName(), aClass1.newInstance());
                            // 将类的最后修改时间纪录
                            LAST_MODIFIED.put(aClass1.getSimpleName(), new ReloadFile(file.getFile(),now));

                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        } catch (InstantiationException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }).start();
    }

}
